#include <stdint.h>
#include <string.h>
#include <bsl_sal.h>
#include <bsl_obj.h>
#include <bsl_errno.h>
#include <bsl_params.h>
#include <bsl_err_internal.h>
#include <crypt_algid.h>
#include <crypt_errno.h>
#include <crypt_eal_implprovider.h>
#include <crypt_eal_provider.h>
#include <crypt_eal_rand.h>
#include <crypt_provider.h>
#include <crypt_default_provderimpl.h>
#include <crypt_params_key.h>
#include <hitls_crypt_type.h>
#include <hitls_cert_type.h>
#include <hitls_type.h>

#include "kaep_provider.h"
#include "kaev1_provider_impl.h"

#include "kaev1_dh.h"
#include "kaev1_digests.h"
#include "kaev1_modes.h"
#include "kaev1_rsa.h"
#include "kaev1_memory.h"

#define KAE_DEFAULT_ATTR "provider=kaev1"

static const CRYPT_EAL_AlgInfo kaeMds[] = {
    {CRYPT_MD_MD5, kaeMd, KAE_DEFAULT_ATTR},    {CRYPT_MD_SM3, kaeMd, KAE_DEFAULT_ATTR},
    {CRYPT_MD_SHA1, kaeMd, KAE_DEFAULT_ATTR},   {CRYPT_MD_SHA256, kaeMd, KAE_DEFAULT_ATTR},
    {CRYPT_MD_SHA224, kaeMd, KAE_DEFAULT_ATTR}, {CRYPT_MD_SHA384, kaeMd, KAE_DEFAULT_ATTR},
    {CRYPT_MD_SHA512, kaeMd, KAE_DEFAULT_ATTR}, CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeModes[] = {
    {CRYPT_CIPHER_SM4_ECB, kaeMode, KAE_DEFAULT_ATTR},    {CRYPT_CIPHER_SM4_CBC, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_SM4_CTR, kaeMode, KAE_DEFAULT_ATTR},    {CRYPT_CIPHER_SM4_XTS, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_SM4_OFB, kaeMode, KAE_DEFAULT_ATTR},    {CRYPT_CIPHER_AES128_ECB, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES128_CBC, kaeMode, KAE_DEFAULT_ATTR}, {CRYPT_CIPHER_AES128_CTR, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES128_OFB, kaeMode, KAE_DEFAULT_ATTR}, {CRYPT_CIPHER_AES192_ECB, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES192_CBC, kaeMode, KAE_DEFAULT_ATTR}, {CRYPT_CIPHER_AES192_CTR, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES192_OFB, kaeMode, KAE_DEFAULT_ATTR}, {CRYPT_CIPHER_AES256_ECB, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES256_CBC, kaeMode, KAE_DEFAULT_ATTR}, {CRYPT_CIPHER_AES256_CTR, kaeMode, KAE_DEFAULT_ATTR},
    {CRYPT_CIPHER_AES256_OFB, kaeMode, KAE_DEFAULT_ATTR}, CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeMgmt[] = {{CRYPT_PKEY_RSA, kaeRsaMgmt, KAE_DEFAULT_ATTR},
                                            {CRYPT_PKEY_DH, kaeDhMgmt, KAE_DEFAULT_ATTR},
                                            CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeAsymCiphers[] = {{CRYPT_PKEY_RSA, kaeRsaCipher, KAE_DEFAULT_ATTR},
                                                   CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeKeyExch[] = {{CRYPT_PKEY_DH, kaeDhExch, KAE_DEFAULT_ATTR}, CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeSigns[] = {{CRYPT_PKEY_RSA, kaeRsaSign, KAE_DEFAULT_ATTR}, CRYPT_EAL_ALGINFO_END};

static const CRYPT_EAL_AlgInfo kaeEalDecoders[] = {
    {BSL_CID_DECODE_UNKNOWN, kaeEalPem2Der,
        "provider=default, inFormat=PEM, outFormat=ASN1"},
    {BSL_CID_DECODE_UNKNOWN, kaeEalPrvP8Enc2P8,
        "provider=default, inFormat=ASN1, inType=PRIKEY_PKCS8_ENCRYPT, outFormat=ASN1, outType=PRIKEY_PKCS8_UNENCRYPT"},
    {CRYPT_PKEY_RSA, kaeEalRsaPrvDer2Key,
        "provider=default, inFormat=ASN1, inType=PRIKEY_RSA, outFormat=OBJECT, outType=LOW_KEY"},
    {CRYPT_PKEY_RSA, kaeEalRsaPubDer2Key,
        "provider=default, inFormat=ASN1, inType=PUBKEY_RSA, outFormat=OBJECT, outType=LOW_KEY"},
    {CRYPT_PKEY_RSA, kaeEalP8Der2RsaKey,
        "provider=default, inFormat=ASN1, inType=PRIKEY_PKCS8_UNENCRYPT, outFormat=OBJECT, outType=LOW_KEY"},
    {CRYPT_PKEY_RSA, kaeEalSubPubKeyDer2RsaKey,
        "provider=default, inFormat=ASN1, inType=PUBKEY_SUBKEY, outFormat=OBJECT, outType=LOW_KEY"},
    {CRYPT_PKEY_RSA, kaeEalSubPubKeyWithoutSeqDer2RsaKey,
        "provider=default, inFormat=ASN1, inType=PUBKEY_SUBKEY_WITHOUT_SEQ, outFormat=OBJECT, outType=LOW_KEY"},
    {BSL_CID_DECODE_UNKNOWN, kaeEalLowKeyObject2PkeyObject,
        "provider=default, inFormat=OBJECT, inType=LOW_KEY, outFormat=OBJECT, outType=HIGH_KEY"},
    CRYPT_EAL_ALGINFO_END
};

static int32_t CRYPT_EAL_DefaultProvQuery(void *provCtx, int32_t operaId, const CRYPT_EAL_AlgInfo **algInfos)
{
    (void)provCtx;
    int32_t ret = CRYPT_SUCCESS;
    switch (operaId) {
        case CRYPT_EAL_OPERAID_SYMMCIPHER:
            *algInfos = kaeModes;
            break;
        case CRYPT_EAL_OPERAID_KEYMGMT:
            *algInfos = kaeMgmt;
            break;
        case CRYPT_EAL_OPERAID_SIGN:
            *algInfos = kaeSigns;
            break;
        case CRYPT_EAL_OPERAID_ASYMCIPHER:
            *algInfos = kaeAsymCiphers;
            break;
        case CRYPT_EAL_OPERAID_KEYEXCH:
            *algInfos = kaeKeyExch;
            break;
        case CRYPT_EAL_OPERAID_HASH:
            *algInfos = kaeMds;
            break;
        case CRYPT_EAL_OPERAID_DECODER:
            *algInfos = kaeEalDecoders;
            break;
        default:
            ret = CRYPT_NOT_SUPPORT;
            break;
    }
    return ret;
}

static void CRYPT_EAL_DefaultProvFree(void *provCtx)
{
    (void) provCtx;
    WdDHDestroyQnodePool();
    WdDigestsDestroyQnodePool();
    WdModesDestroyQnodePool();
    WdRsaDestroyQnodePool();
}

static CRYPT_EAL_Func defProvOutFuncs[] = {{CRYPT_EAL_PROVCB_QUERY, CRYPT_EAL_DefaultProvQuery},
                                           {CRYPT_EAL_PROVCB_FREE, CRYPT_EAL_DefaultProvFree},
                                           {CRYPT_EAL_PROVCB_CTRL, NULL},
                                           {CRYPT_EAL_PROVCB_GETCAPS, NULL},
                                           CRYPT_EAL_FUNC_END};

int32_t CRYPT_EAL_ProviderInit(CRYPT_EAL_ProvMgrCtx *mgrCtx, BSL_Param *param, CRYPT_EAL_Func *capFuncs,
                               CRYPT_EAL_Func **outFuncs, void **provCtx)
{
    int32_t ret = CRYPT_SUCCESS;
    ret = WdDHInitQnodePool();
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        goto EXIT;
    }

    ret = WdDigestsInitQnodePool();
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        goto EXIT;
    }

    ret = WdModesInitQnodePool();
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        goto EXIT;
    }

    ret = WdRsaInitQnodePool();
    if (ret != CRYPT_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        goto EXIT;
    }

    *outFuncs = defProvOutFuncs;
    return CRYPT_SUCCESS;
EXIT:
    CRYPT_EAL_DefaultProvFree(NULL);
    return ret;
}